This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / web / src / routes / [handle] / +layout.ts
2.7 kB 75 lines
1import { error, redirect } from "@sveltejs/kit"; 2import { createBobbinClient } from "$lib/api/client"; 3import { resolveMiniDoc } from "$lib/api/identity"; 4import { getProfile, type ProfileRecord } from "$lib/api/records"; 5import { count } from "$lib/api/count"; 6import { parallel, toHttpError, httpStatusFor } from "$lib/api/load"; 7import { ClientResponseError } from "$lib/api/client"; 8import { getFollowRkey } from "$lib/api/graph"; 9import type { ProfileCounts } from "$lib/components/profile/types"; 10import type { LayoutLoad } from "./$types"; 11 12export const load: LayoutLoad = async (event) => { 13 const parent = await event.parent(); 14 const identifier = decodeURIComponent(event.params.handle); 15 16 // rejects bare words so unrelated paths 404 instead of resolving as actors 17 if (!identifier.startsWith("did:") && !identifier.includes(".")) { 18 error(404, "Not found"); 19 } 20 21 const ctx = createBobbinClient({ serviceUrl: parent.publicConfig.bobbinUrl, fetch: event.fetch }); 22 const doc = await resolveMiniDoc(ctx, identifier).catch((cause) => 23 toHttpError(cause, "Could not resolve user") 24 ); 25 26 // redirects dids and stale handles to the canonical handle url 27 const canonical = doc.handle && !doc.handle.endsWith(".invalid") ? doc.handle : null; 28 if (canonical && identifier.toLowerCase() !== canonical.toLowerCase()) { 29 redirect(307, `/${canonical}${event.url.search}`); 30 } 31 32 const did = doc.did; 33 const viewerDid = parent.auth?.did; 34 35 let profile: ProfileRecord | null = null; 36 try { 37 profile = (await getProfile(ctx, did)).value; 38 } catch (cause) { 39 if (!(cause instanceof ClientResponseError && httpStatusFor(cause) === 404)) { 40 toHttpError(cause, "Could not load profile"); 41 } 42 } 43 44 const raw = await parallel({ 45 repos: count(ctx, "sh.tangled.repo.countRepos", did), 46 strings: count(ctx, "sh.tangled.string.countStrings", did), 47 stars: count(ctx, "sh.tangled.feed.countStarsBy", did), 48 followers: count(ctx, "sh.tangled.graph.countFollows", did), 49 following: count(ctx, "sh.tangled.graph.countFollowsBy", did), 50 vouches: count(ctx, "sh.tangled.graph.countVouches", did), 51 viewerFollowRkey: 52 viewerDid && viewerDid !== did 53 ? getFollowRkey(ctx, viewerDid, did).catch(() => null) 54 : Promise.resolve(null) 55 }); 56 57 const counts: ProfileCounts = { 58 repos: raw.repos.count, 59 strings: raw.strings.count, 60 stars: raw.stars.count, 61 followers: raw.followers.count, 62 following: raw.following.count, 63 vouches: raw.vouches.count 64 }; 65 66 const notJoined = !profile && Object.values(counts).every((n) => n === 0); 67 68 return { 69 identity: { did, handle: doc.handle, avatar: doc.avatar }, 70 profile, 71 counts, 72 viewerFollowRkey: raw.viewerFollowRkey, 73 notJoined 74 }; 75};